//  filesystem path.cpp  -------------------------------------------------------------  //

//  Copyright Beman Dawes 2008

//  Distributed under the Boost Software License, Version 1.0.
//  See http://www.boost.org/LICENSE_1_0.txt

//  Library home page: http://www.boost.org/libs/filesystem

#include "platform_config.hpp"

//  Old standard library configurations, particularly MingGW, don't support wide strings.
//  Report this with an explicit error message.
#include <boost/config.hpp>
#if defined(BOOST_NO_STD_WSTRING)
#error Configuration not supported: Boost.Filesystem V3 and later requires std::wstring support
#endif

#include <boost/filesystem/config.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp> // for filesystem_error
#include <boost/scoped_array.hpp>
#include <boost/system/error_code.hpp>
#include <boost/assert.hpp>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstddef>
#include <cstring>
#include <cassert>

#ifdef BOOST_WINDOWS_API
#include "windows_file_codecvt.hpp"
#include <windows.h>
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
#endif

#ifdef BOOST_FILESYSTEM_DEBUG
#include <iostream>
#include <iomanip>
#endif

namespace fs = boost::filesystem;

using boost::filesystem::path;

using std::string;
using std::wstring;

using boost::system::error_code;

//--------------------------------------------------------------------------------------//
//                                                                                      //
//                                class path helpers                                    //
//                                                                                      //
//--------------------------------------------------------------------------------------//

namespace {
//------------------------------------------------------------------------------------//
//                        miscellaneous class path helpers                            //
//------------------------------------------------------------------------------------//

typedef path::value_type value_type;
typedef path::string_type string_type;
typedef string_type::size_type size_type;

#ifdef BOOST_WINDOWS_API

const wchar_t* const separators = L"/\\";
const wchar_t* separator_string = L"/";
const wchar_t* preferred_separator_string = L"\\";
const wchar_t colon = L':';
const wchar_t questionmark = L'?';

inline bool is_letter(wchar_t c)
{
    return (c >= L'a' && c <= L'z') || (c >= L'A' && c <= L'Z');
}

#else

const char* const separators = "/";
const char* separator_string = "/";
const char* preferred_separator_string = "/";

#endif

// pos is position of the separator
bool is_root_separator(string_type const& str, size_type pos);

// Returns: 0 if str itself is filename (or empty)
// end_pos is past-the-end position
size_type filename_pos(string_type const& str, size_type end_pos);

// Returns: npos if no root_directory found
size_type root_directory_start(string_type const& path, size_type size);

void first_element(
    string_type const& src,
    size_type& element_pos,
    size_type& element_size,
#if !BOOST_WORKAROUND(BOOST_MSVC, <= 1310) // VC++ 7.1
    size_type size = string_type::npos
#else
    size_type size = -1
#endif
);

} // unnamed namespace

//--------------------------------------------------------------------------------------//
//                                                                                      //
//                            class path implementation                                 //
//                                                                                      //
//--------------------------------------------------------------------------------------//

namespace boost {
namespace filesystem {

BOOST_FILESYSTEM_DECL path& path::operator/=(const path& p)
{
    if (p.empty())
        return *this;
    if (this == &p) // self-append
    {
        path rhs(p);
        if (!detail::is_directory_separator(rhs.m_pathname[0]))
            m_append_separator_if_needed();
        m_pathname += rhs.m_pathname;
    }
    else
    {
        if (!detail::is_directory_separator(*p.m_pathname.begin()))
            m_append_separator_if_needed();
        m_pathname += p.m_pathname;
    }
    return *this;
}

BOOST_FILESYSTEM_DECL path& path::operator/=(const value_type* ptr)
{
    if (!*ptr)
        return *this;
    if (ptr >= m_pathname.data() && ptr < m_pathname.data() + m_pathname.size()) // overlapping source
    {
        path rhs(ptr);
        if (!detail::is_directory_separator(rhs.m_pathname[0]))
            m_append_separator_if_needed();
        m_pathname += rhs.m_pathname;
    }
    else
    {
        if (!detail::is_directory_separator(*ptr))
            m_append_separator_if_needed();
        m_pathname += ptr;
    }
    return *this;
}

#ifdef BOOST_WINDOWS_API

BOOST_FILESYSTEM_DECL path path::generic_path() const
{
    path tmp(*this);
    std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/');
    return tmp;
}

#endif // BOOST_WINDOWS_API

BOOST_FILESYSTEM_DECL int path::compare(const path& p) const BOOST_NOEXCEPT
{
    return detail::lex_compare(begin(), end(), p.begin(), p.end());
}

//  m_append_separator_if_needed  ----------------------------------------------------//

BOOST_FILESYSTEM_DECL path::string_type::size_type path::m_append_separator_if_needed()
{
    if (!m_pathname.empty() &&
#ifdef BOOST_WINDOWS_API
        *(m_pathname.end() - 1) != colon &&
#endif
        !detail::is_directory_separator(*(m_pathname.end() - 1)))
    {
        string_type::size_type tmp(m_pathname.size());
        m_pathname += preferred_separator;
        return tmp;
    }
    return 0;
}

//  m_erase_redundant_separator  -----------------------------------------------------//

BOOST_FILESYSTEM_DECL void path::m_erase_redundant_separator(string_type::size_type sep_pos)
{
    if (sep_pos                                  // a separator was added
        && sep_pos < m_pathname.size()           // and something was appended
        && (m_pathname[sep_pos + 1] == separator // and it was also separator
#ifdef BOOST_WINDOWS_API
            || m_pathname[sep_pos + 1] == preferred_separator // or preferred_separator
#endif
            ))
    {
        m_pathname.erase(m_pathname.begin() + sep_pos); // erase the added separator
    }
}

//  modifiers  -----------------------------------------------------------------------//

#ifdef BOOST_WINDOWS_API
BOOST_FILESYSTEM_DECL path& path::make_preferred()
{
    std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\');
    return *this;
}
#endif

BOOST_FILESYSTEM_DECL path& path::remove_filename()
{
    size_type end_pos(m_parent_path_end());
    if (end_pos == string_type::npos)
        end_pos = 0u;
    m_pathname.erase(m_pathname.begin() + end_pos, m_pathname.end());
    return *this;
}

BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator()
{
    if (!m_pathname.empty() && detail::is_directory_separator(m_pathname[m_pathname.size() - 1]))
        m_pathname.erase(m_pathname.end() - 1);
    return *this;
}

BOOST_FILESYSTEM_DECL path& path::replace_extension(const path& new_extension)
{
    // erase existing extension, including the dot, if any
    m_pathname.erase(m_pathname.size() - extension().m_pathname.size());

    if (!new_extension.empty())
    {
        // append new_extension, adding the dot if necessary
        if (new_extension.m_pathname[0] != dot)
            m_pathname.push_back(dot);
        m_pathname.append(new_extension.m_pathname);
    }

    return *this;
}

//  decomposition  -------------------------------------------------------------------//

BOOST_FILESYSTEM_DECL path path::root_path() const
{
    path temp(root_name());
    if (!root_directory().empty())
        temp.m_pathname += root_directory().c_str();
    return temp;
}

BOOST_FILESYSTEM_DECL path path::root_name() const
{
    iterator itr(begin());

    return
    (
        itr.m_pos != m_pathname.size() &&
        (
            (itr.m_element.m_pathname.size() > 1 && detail::is_directory_separator(itr.m_element.m_pathname[0]) && detail::is_directory_separator(itr.m_element.m_pathname[1]))
#ifdef BOOST_WINDOWS_API
            || itr.m_element.m_pathname[itr.m_element.m_pathname.size() - 1] == colon
#endif
        )
    ) ? itr.m_element : path();
}

BOOST_FILESYSTEM_DECL path path::root_directory() const
{
    size_type pos(root_directory_start(m_pathname, m_pathname.size()));
    return pos == string_type::npos ? path() : path(m_pathname.c_str() + pos, m_pathname.c_str() + pos + 1);
}

BOOST_FILESYSTEM_DECL path path::relative_path() const
{
    iterator itr(begin());

    for (;
        itr.m_pos != m_pathname.size() &&
        (
            detail::is_directory_separator(itr.m_element.m_pathname[0])
#ifdef BOOST_WINDOWS_API
            || itr.m_element.m_pathname[itr.m_element.m_pathname.size() - 1] == colon
#endif
        );
        ++itr)
    {
    }

    return path(m_pathname.c_str() + itr.m_pos);
}

BOOST_FILESYSTEM_DECL string_type::size_type path::m_parent_path_end() const
{
    size_type end_pos(filename_pos(m_pathname, m_pathname.size()));

    bool filename_was_separator = !m_pathname.empty() && detail::is_directory_separator(m_pathname[end_pos]);

    // skip separators unless root directory
    size_type root_dir_pos(root_directory_start(m_pathname, end_pos));
    for (;
         end_pos > 0 && (end_pos - 1) != root_dir_pos && detail::is_directory_separator(m_pathname[end_pos - 1]);
         --end_pos)
    {
    }

    return (end_pos == 1 && root_dir_pos == 0 && filename_was_separator) ? string_type::npos : end_pos;
}

BOOST_FILESYSTEM_DECL path path::parent_path() const
{
    size_type end_pos(m_parent_path_end());
    return end_pos == string_type::npos ? path() : path(m_pathname.c_str(), m_pathname.c_str() + end_pos);
}

BOOST_FILESYSTEM_DECL path path::filename() const
{
    size_type pos(filename_pos(m_pathname, m_pathname.size()));
    return (!m_pathname.empty() && pos && detail::is_directory_separator(m_pathname[pos]) && !is_root_separator(m_pathname, pos)) ? detail::dot_path() : path(m_pathname.c_str() + pos);
}

BOOST_FILESYSTEM_DECL path path::stem() const
{
    path name(filename());
    if (name == detail::dot_path() || name == detail::dot_dot_path())
        return name;
    size_type pos(name.m_pathname.rfind(dot));
    return pos == string_type::npos ? name : path(name.m_pathname.c_str(), name.m_pathname.c_str() + pos);
}

BOOST_FILESYSTEM_DECL path path::extension() const
{
    path name(filename());
    if (name == detail::dot_path() || name == detail::dot_dot_path())
        return path();
    size_type pos(name.m_pathname.rfind(dot));
    return pos == string_type::npos ? path() : path(name.m_pathname.c_str() + pos);
}

//  lexical operations  --------------------------------------------------------------//

namespace detail {
// C++14 provides a mismatch algorithm with four iterator arguments(), but earlier
// standard libraries didn't, so provide this needed functionality.
inline std::pair< path::iterator, path::iterator > mismatch(path::iterator it1, path::iterator it1end, path::iterator it2, path::iterator it2end)
{
    for (; it1 != it1end && it2 != it2end && *it1 == *it2;)
    {
        ++it1;
        ++it2;
    }
    return std::make_pair(it1, it2);
}
} // namespace detail

BOOST_FILESYSTEM_DECL path path::lexically_relative(const path& base) const
{
    path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end();
    std::pair< path::iterator, path::iterator > mm = detail::mismatch(b, e, base_b, base_e);
    if (mm.first == b && mm.second == base_b)
        return path();
    if (mm.first == e && mm.second == base_e)
        return detail::dot_path();

    std::ptrdiff_t n = 0;
    for (; mm.second != base_e; ++mm.second)
    {
        path const& p = *mm.second;
        if (p == detail::dot_dot_path())
            --n;
        else if (!p.empty() && p != detail::dot_path())
            ++n;
    }
    if (n < 0)
        return path();
    if (n == 0 && (mm.first == e || mm.first->empty()))
        return detail::dot_path();

    path tmp;
    for (; n > 0; --n)
        tmp /= detail::dot_dot_path();
    for (; mm.first != e; ++mm.first)
        tmp /= *mm.first;
    return tmp;
}

//  normal  --------------------------------------------------------------------------//

BOOST_FILESYSTEM_DECL path path::lexically_normal() const
{
    if (m_pathname.empty())
        return *this;

    path temp;
    iterator start(begin());
    iterator last(end());
    iterator stop(last--);
    for (iterator itr(start); itr != stop; ++itr)
    {
        // ignore "." except at start and last
        if (itr->native().size() == 1 && (itr->native())[0] == dot && itr != start && itr != last)
            continue;

        // ignore a name and following ".."
        if (!temp.empty() && itr->native().size() == 2 && (itr->native())[0] == dot && (itr->native())[1] == dot) // dot dot
        {
            string_type lf(temp.filename().native());
            string_type::size_type lf_size = lf.size();
            if (lf_size > 0 && (lf_size != 1 || (lf[0] != dot && lf[0] != separator)) && (lf_size != 2 || (lf[0] != dot && lf[1] != dot
#ifdef BOOST_WINDOWS_API
                                                                                                           && lf[1] != colon
#endif
                                                                                                           )))
            {
                temp.remove_filename();
                //// if not root directory, must also remove "/" if any
                //if (temp.native().size() > 0
                //  && temp.native()[temp.native().size()-1]
                //    == separator)
                //{
                //  string_type::size_type rds(
                //    root_directory_start(temp.native(), temp.native().size()));
                //  if (rds == string_type::npos
                //    || rds != temp.native().size()-1)
                //  {
                //    temp.m_pathname.erase(temp.native().size()-1);
                //  }
                //}

                iterator next(itr);
                if (temp.empty() && ++next != stop && next == last && *last == detail::dot_path())
                {
                    temp /= detail::dot_path();
                }
                continue;
            }
        }

        temp /= *itr;
    }

    if (temp.empty())
        temp /= detail::dot_path();
    return temp;
}

} // namespace filesystem
} // namespace boost

//--------------------------------------------------------------------------------------//
//                                                                                      //
//                         class path helpers implementation                            //
//                                                                                      //
//--------------------------------------------------------------------------------------//

namespace {

//  is_root_separator  ---------------------------------------------------------------//

// pos is position of the separator
bool is_root_separator(string_type const& str, size_type pos)
{
    BOOST_ASSERT_MSG(!str.empty() && fs::detail::is_directory_separator(str[pos]), "precondition violation");

    // subsequent logic expects pos to be for leftmost slash of a set
    while (pos > 0 && fs::detail::is_directory_separator(str[pos - 1]))
        --pos;

    //  "/" [...]
    if (pos == 0)
        return true;

#ifdef BOOST_WINDOWS_API
    //  "c:/" [...]
    if (pos == 2 && is_letter(str[0]) && str[1] == colon)
        return true;
#endif

    //  "//" name "/"
    if (pos < 3 || !fs::detail::is_directory_separator(str[0]) || !fs::detail::is_directory_separator(str[1]))
        return false;

    return str.find_first_of(separators, 2) == pos;
}

//  filename_pos  --------------------------------------------------------------------//

// end_pos is past-the-end position
// return 0 if str itself is filename (or empty)
size_type filename_pos(string_type const& str, size_type end_pos)
{
    // case: "//"
    if (end_pos == 2 && fs::detail::is_directory_separator(str[0]) && fs::detail::is_directory_separator(str[1]))
        return 0;

    // case: ends in "/"
    if (end_pos && fs::detail::is_directory_separator(str[end_pos - 1]))
        return end_pos - 1;

    // set pos to start of last element
    size_type pos(str.find_last_of(separators, end_pos - 1));

#ifdef BOOST_WINDOWS_API
    if (pos == string_type::npos && end_pos > 1)
        pos = str.find_last_of(colon, end_pos - 2);
#endif

    return (pos == string_type::npos                                     // path itself must be a filename (or empty)
            || (pos == 1 && fs::detail::is_directory_separator(str[0]))) // or net
        ?
        0 // so filename is entire string
        :
        pos + 1; // or starts after delimiter
}

//  root_directory_start  ------------------------------------------------------------//

// return npos if no root_directory found
size_type root_directory_start(string_type const& path, size_type size)
{

#ifdef BOOST_WINDOWS_API
    // case "c:/"
    if (size > 2 && path[1] == colon && fs::detail::is_directory_separator(path[2]))
        return 2;
#endif

    // case "//"
    if (size == 2 && fs::detail::is_directory_separator(path[0]) && fs::detail::is_directory_separator(path[1]))
        return string_type::npos;

#ifdef BOOST_WINDOWS_API
    // case "\\?\"
    if (size > 4 && fs::detail::is_directory_separator(path[0]) && fs::detail::is_directory_separator(path[1]) && path[2] == questionmark && fs::detail::is_directory_separator(path[3]))
    {
        string_type::size_type pos(path.find_first_of(separators, 4));
        return pos < size ? pos : string_type::npos;
    }
#endif

    // case "//net {/}"
    if (size > 3 && fs::detail::is_directory_separator(path[0]) && fs::detail::is_directory_separator(path[1]) && !fs::detail::is_directory_separator(path[2]))
    {
        string_type::size_type pos(path.find_first_of(separators, 2));
        return pos < size ? pos : string_type::npos;
    }

    // case "/"
    if (size > 0 && fs::detail::is_directory_separator(path[0]))
        return 0;

    return string_type::npos;
}

//  first_element --------------------------------------------------------------------//

//   sets pos and len of first element, excluding extra separators
//   if src.empty(), sets pos,len, to 0,0.
void first_element(
    string_type const& src,
    size_type& element_pos,
    size_type& element_size,
    size_type size)
{
    if (size == string_type::npos)
        size = src.size();
    element_pos = 0;
    element_size = 0;
    if (src.empty())
        return;

    string_type::size_type cur(0);

    // deal with // [network]
    if (size >= 2 && fs::detail::is_directory_separator(src[0]) && fs::detail::is_directory_separator(src[1]) && (size == 2 || !fs::detail::is_directory_separator(src[2])))
    {
        cur += 2;
        element_size += 2;
    }

    // leading (not non-network) separator
    else if (fs::detail::is_directory_separator(src[0]))
    {
        ++element_size;
        // bypass extra leading separators
        while (cur + 1 < size && fs::detail::is_directory_separator(src[cur + 1]))
        {
            ++cur;
            ++element_pos;
        }
        return;
    }

    // at this point, we have either a plain name, a network name,
    // or (on Windows only) a device name

    // find the end
    while (cur < size
#ifdef BOOST_WINDOWS_API
           && src[cur] != colon
#endif
           && !fs::detail::is_directory_separator(src[cur]))
    {
        ++cur;
        ++element_size;
    }

#ifdef BOOST_WINDOWS_API
    if (cur == size)
        return;
    // include device delimiter
    if (src[cur] == colon)
    {
        ++element_size;
    }
#endif
}

} // unnamed namespace

namespace boost {
namespace filesystem {
namespace detail {

BOOST_FILESYSTEM_DECL
int lex_compare(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2)
{
    for (; first1 != last1 && first2 != last2;)
    {
        if (first1->native() < first2->native())
            return -1;
        if (first2->native() < first1->native())
            return 1;
        BOOST_ASSERT(first2->native() == first1->native());
        ++first1;
        ++first2;
    }
    if (first1 == last1 && first2 == last2)
        return 0;
    return first1 == last1 ? -1 : 1;
}

BOOST_FILESYSTEM_DECL
const path& dot_path()
{
#ifdef BOOST_WINDOWS_API
    static const fs::path dot_pth(L".");
#else
    static const fs::path dot_pth(".");
#endif
    return dot_pth;
}

BOOST_FILESYSTEM_DECL
const path& dot_dot_path()
{
#ifdef BOOST_WINDOWS_API
    static const fs::path dot_dot(L"..");
#else
    static const fs::path dot_dot("..");
#endif
    return dot_dot;
}

} // namespace detail

//--------------------------------------------------------------------------------------//
//                                                                                      //
//                        class path::iterator implementation                           //
//                                                                                      //
//--------------------------------------------------------------------------------------//

BOOST_FILESYSTEM_DECL path::iterator path::begin() const
{
    iterator itr;
    itr.m_path_ptr = this;
    size_type element_size;
    first_element(m_pathname, itr.m_pos, element_size);
    itr.m_element = m_pathname.substr(itr.m_pos, element_size);
    if (itr.m_element.m_pathname == preferred_separator_string)
        itr.m_element.m_pathname = separator_string; // needed for Windows, harmless on POSIX
    return itr;
}

BOOST_FILESYSTEM_DECL path::iterator path::end() const
{
    iterator itr;
    itr.m_path_ptr = this;
    itr.m_pos = m_pathname.size();
    return itr;
}

BOOST_FILESYSTEM_DECL void path::m_path_iterator_increment(path::iterator& it)
{
    BOOST_ASSERT_MSG(it.m_pos < it.m_path_ptr->m_pathname.size(), "path::basic_iterator increment past end()");

    // increment to position past current element; if current element is implicit dot,
    // this will cause it.m_pos to represent the end iterator
    it.m_pos += it.m_element.m_pathname.size();

    // if the end is reached, we are done
    if (it.m_pos == it.m_path_ptr->m_pathname.size())
    {
        it.m_element.clear(); // aids debugging, may release unneeded memory
        return;
    }

    // both POSIX and Windows treat paths that begin with exactly two separators specially
    bool was_net = it.m_element.m_pathname.size() > 2 &&
        detail::is_directory_separator(it.m_element.m_pathname[0]) &&
        detail::is_directory_separator(it.m_element.m_pathname[1]) &&
        !detail::is_directory_separator(it.m_element.m_pathname[2]);

    // process separator (Windows drive spec is only case not a separator)
    if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
    {
        // detect root directory
        if (was_net
#ifdef BOOST_WINDOWS_API
            // case "c:/"
            || it.m_element.m_pathname[it.m_element.m_pathname.size() - 1] == colon
#endif
        )
        {
            it.m_element.m_pathname = separator; // generic format; see docs
            return;
        }

        // skip separators until it.m_pos points to the start of the next element
        while (it.m_pos != it.m_path_ptr->m_pathname.size() && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
        {
            ++it.m_pos;
        }

        // detect trailing separator, and treat it as ".", per POSIX spec
        if (it.m_pos == it.m_path_ptr->m_pathname.size() && !is_root_separator(it.m_path_ptr->m_pathname, it.m_pos - 1))
        {
            --it.m_pos;
            it.m_element = detail::dot_path();
            return;
        }
    }

    // get m_element
    size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos);
    if (end_pos == string_type::npos)
        end_pos = it.m_path_ptr->m_pathname.size();
    it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
}

BOOST_FILESYSTEM_DECL void path::m_path_iterator_decrement(path::iterator& it)
{
    BOOST_ASSERT_MSG(it.m_pos, "path::iterator decrement past begin()");

    size_type end_pos = it.m_pos;

    // if at end and there was a trailing non-root '/', return "."
    if (it.m_pos == it.m_path_ptr->m_pathname.size() && it.m_path_ptr->m_pathname.size() > 1 && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) && !is_root_separator(it.m_path_ptr->m_pathname, it.m_pos - 1))
    {
        --it.m_pos;
        it.m_element = detail::dot_path();
        return;
    }

    size_type root_dir_pos = root_directory_start(it.m_path_ptr->m_pathname, end_pos);

    // skip separators unless root directory
    for (;
        end_pos > 0 && (end_pos - 1) != root_dir_pos && detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos - 1]);
        --end_pos)
    {
    }

    it.m_pos = filename_pos(it.m_path_ptr->m_pathname, end_pos);
    it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
    if (it.m_element.m_pathname == preferred_separator_string) // needed for Windows, harmless on POSIX
        it.m_element.m_pathname = separator_string;            // generic format; see docs
}

} // namespace filesystem
} // namespace boost

namespace {

//------------------------------------------------------------------------------------//
//                                locale helpers                                      //
//------------------------------------------------------------------------------------//

//  Prior versions of these locale and codecvt implementations tried to take advantage
//  of static initialization where possible, kept a local copy of the current codecvt
//  facet (to avoid codecvt() having to call use_facet()), and was not multi-threading
//  safe (again for efficiency).
//
//  This was error prone, and required different implementation techniques depending
//  on the compiler and also whether static or dynamic linking was used. Furthermore,
//  users could not easily provide their multi-threading safe wrappers because the
//  path interface requires the implementation itself to call codecvt() to obtain the
//  default facet, and the initialization of the static within path_locale() could race.
//
//  The code below is portable to all platforms, is much simpler, and hopefully will be
//  much more robust. Timing tests (on Windows, using a Visual C++ release build)
//  indicated the current code is roughly 9% slower than the previous code, and that
//  seems a small price to pay for better code that is easier to use.

std::locale default_locale()
{
#if defined(BOOST_WINDOWS_API)
    std::locale global_loc = std::locale();
    return std::locale(global_loc, new windows_file_codecvt);
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
    // "All BSD system functions expect their string parameters to be in UTF-8 encoding
    // and nothing else." See
    // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html
    //
    // "The kernel will reject any filename that is not a valid UTF-8 string, and it will
    // even be normalized (to Unicode NFD) before stored on disk, at least when using HFS.
    // The right way to deal with it would be to always convert the filename to UTF-8
    // before trying to open/create a file." See
    // http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html
    //
    // "How a file name looks at the API level depends on the API. Current Carbon APIs
    // handle file names as an array of UTF-16 characters; POSIX ones handle them as an
    // array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk
    // depends on the disk format; HFS+ uses UTF-16, but that's not important in most
    // cases." See
    // http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html
    //
    // Many thanks to Peter Dimov for digging out the above references!

    std::locale global_loc = std::locale();
    return std::locale(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
#else // Other POSIX
    // ISO C calls std::locale("") "the locale-specific native environment", and this
    // locale is the default for many POSIX-based operating systems such as Linux.
    return std::locale("");
#endif
}

// std::locale("") construction, needed on non-Apple POSIX systems, can throw
// (if environmental variables LC_MESSAGES or LANG are wrong, for example), so
// path_locale() provides lazy initialization via a local static to ensure that any
// exceptions occur after main() starts and so can be caught. Furthermore,
// path_locale() is only called if path::codecvt() or path::imbue() are themselves
// actually called, ensuring that an exception will only be thrown if std::locale("")
// is really needed.
std::locale& path_locale()
{
    // [locale] paragraph 6: Once a facet reference is obtained from a locale object by
    // calling use_facet<>, that reference remains usable, and the results from member
    // functions of it may be cached and re-used, as long as some locale object refers
    // to that facet.
    static std::locale loc(default_locale());
#ifdef BOOST_FILESYSTEM_DEBUG
    std::cout << "***** path_locale() called" << std::endl;
#endif
    return loc;
}

} // unnamed namespace

//--------------------------------------------------------------------------------------//
//              path::codecvt() and path::imbue() implementation                        //
//--------------------------------------------------------------------------------------//

namespace boost {
namespace filesystem {

BOOST_FILESYSTEM_DECL const path::codecvt_type& path::codecvt()
{
#ifdef BOOST_FILESYSTEM_DEBUG
    std::cout << "***** path::codecvt() called" << std::endl;
#endif
    BOOST_ASSERT_MSG(&path_locale(), "boost::filesystem::path locale initialization error");

    return std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(path_locale());
}

BOOST_FILESYSTEM_DECL std::locale path::imbue(const std::locale& loc)
{
#ifdef BOOST_FILESYSTEM_DEBUG
    std::cout << "***** path::imbue() called" << std::endl;
#endif
    std::locale temp(path_locale());
    path_locale() = loc;
    return temp;
}

} // namespace filesystem
} // namespace boost
